#!/usr/bin/python
# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

__date__ = u"23 juil. 2009"
__author__ = u"INRIA pyLot, UPPA"

__all__ = [u'GuiProcessHandler']

from pyLot.core import check_type, check_value, hardbook, error, get_data

from _pylot.qtgui.styles.colors import PYLOT_COLORS
from _pylot.qtgui.styles.charformats import PYLOT_CHAR_FORMATS
from _pylot.qtgui.tools import generate_pyfile_from_uifile
from _pylot.qtgui.default.impl_widget import Widget
from _pylot.qtgui.widgets.gui_process import GuiProcess

from Qt4 import QtGui
from Qt4.QtCore import (SIGNAL, QVariant, Qt)

################################################################################
generate_pyfile_from_uifile(__name__,
  src=hardbook(u'uifiles', u'processhandler_pref.ui'),
  dest=get_data(__name__, u'_gui_processhandler_pref.py'))
generate_pyfile_from_uifile(__name__,
  src=hardbook(u'uifiles', u'processhandler.ui'),
  dest=get_data(__name__, u'_gui_processhandler.py'))
################################################################################

from _gui_processhandler_pref import Ui_GuiProcessHandlerPref
class GuiProcessHandlerPref (QtGui.QWidget, Widget, Ui_GuiProcessHandlerPref):

  identifier = 'pyLot.widget.gui_processhandler_pref'

  def __init__ (self):
    QtGui.QWidget.__init__(self)
    Widget.__init__(self)
    self.setupUi(self)

from _gui_processhandler import Ui_GuiProcessHandler
class GuiProcessHandler(QtGui.QWidget, Widget, Ui_GuiProcessHandler):
  u"""

  See designer file :file:`processhandler.ui` to know actions provided
  by this widget

  **SIGNALS:**
    - SIGNAL processTabClosed [QString]: this signal is sent when a tab is closed.
      QString is the corresponding process label
    
    - SIGNAL processInBackgroundChanged [boolean]: this signal is sent when
      state of "process in background" check box is changed.

    - SIGNAL maxProcessChanged [integer]: this signal is sent when
      value of "max process" spinbox changes.
  """
  identifier = 'pyLot.widget.gui_processhandler'

  def __init__(self, main_process=None, char_colors=PYLOT_COLORS,
               char_formats=PYLOT_CHAR_FORMATS):
    QtGui.QWidget.__init__(self)
    self.setupUi(self)
    Widget.__init__(self)

    # TODO: property and value check
    self.char_colors = char_colors
    self.char_formats = char_formats

    self._process_widgets = {} # dict of GuiProcess

    self._createPreferences()

    self.tab_process = QtGui.QTabWidget(self)
    self.layout_process.addWidget(self.tab_process)
    u"""main process widget, always exists, cannot be closed"""
    if main_process is None :
      self.main_process = self.createTab(u'Main process')
    else :
      self.main_process = main_process
      self.addTab(main_process, u'Main process')
    self.append(self.main_process)

    self._process_splitter_state = self.main_process.getSplitterState()
    self._process_splitter_horizontal = True

    self.changeTabState(self.main_process, u'success')
    self.action_close_process.setDisabled(True)

    self._createActions()
    self._createConnexions()
    self._createToolBars()
    self._createMenus()

    self.activateActions()

  def _createToolBars(self):
    #tool_bar_process
    tb = QtGui.QToolBar()
    tb.setObjectName(u'tool_bar_process')
    self.tool_bar_process = tb
    self.addToolBar_(self.tool_bar_process)

  def _createMenus(self):
    #tool_bar_process
    menu = QtGui.QMenu()
    menu.setObjectName(u'menu_process')
    self.menu_process = menu
    self.addMenu_(self.menu_process)

  def _createPreferences(self):
    self._pref = GuiProcessHandlerPref()

  def _createActions(self):
    u""" done in designer file """

  def _createConnexions(self):
    self.connect(self.cb_view_locked,
                 SIGNAL('stateChanged(int)'), self._onCbViewLockedChanged)
    self.connect(self.action_cancel_process, SIGNAL("triggered()"),
                 self._onActionCancelProcessTriggered)
    self.connect(self.action_close_process, SIGNAL("triggered()"),
                 self._onActionCloseProcessTriggered)
    self.connect(self.tab_process, SIGNAL("currentChanged(int)"),
                 self._onTabProcessCurrentChanged)

    self.connect(self.action_clear_output, SIGNAL("triggered()"),
                 self.clearOutput)
    self.connect(self.action_clear_error, SIGNAL("triggered()"),
                 self.clearError)
    self.connect(self.action_clear_both_outputs,
                 SIGNAL("triggered()"), self.clearBothOutputs)
    self.connect(self.action_clear_all, SIGNAL("triggered()"), self.clearAll)

    # Preferences' signals
    self.connect(self.pref.rb_horizontal, SIGNAL('toggled(bool)'),
                 self.processSplitterHorizontalChanged)
    self.connect(self.pref.cb_process_in_background, SIGNAL('stateChanged(int)'),
                 self._onCbProcessInBackgroundChanged)
    self.connect(self.pref.sb_max_process, SIGNAL('valueChanged(int)'),
                 self._onMaxProcessChanged)

  # ============================================================================
  # Methods implemented
  # ============================================================================

  def _updateData(self):
    pass

  def _refresh(self):
    pass

  def _decorate (self):
    self._setIcons([
      (self.action_cancel_process, u"cancel.png"),
      (self.action_clear_all, u"toolsClearAll.png"),
      (self.action_clear_both_outputs, u"toolsClearAll.png"),
      (self.action_clear_error, u'toolsClearError.png'),
      (self.action_clear_output, u"toolsClearOutput.png"),
      (self.action_close_process, u"closeTabKonqueror.png"),
      ])

  def _translate (self):
    self._setTexts([
      (self.pref.rb_vertical, tr_(u"Vertical")),
      (self.pref.rb_horizontal, tr_(u"Horizontal")),
      (self.tool_bar_process, tr_(u"Process Toolbar")),
      (self.menu_process, tr_(u"Process")),
      ])

  def _fillToolBars(self, level):
    self._fill(self.tool_bar_process, [
      self.action_close_process,
      self.action_cancel_process,
      u'-',
      self.action_clear_output,
      self.action_clear_error,
      self.action_clear_both_outputs
      ])

  def _fillMenus(self, level):
    self._fill(self.menu_process, [
      self.action_close_process,
      self.action_cancel_process,
      u'-',
      self.action_clear_output,
      self.action_clear_error,
      self.action_clear_both_outputs
      ])

  def _quit(self):
    self.moveProcessSplitters()
    return True

  def _switchUserLevel(self, level):
    pass


  # ============================================================================
  # Widget Settings
  # ============================================================================

  def _readSettings(self, settings, common):
    settings.beginGroup(u"GuiProcessHandler")

    self._process_splitter_state = settings.value(u"ProcessSplitterState",
      QVariant(self._process_splitter_state)).toByteArray()

    self.main_process.splitter.restoreState(self._process_splitter_state)

    orientation = self.main_process.splitter.orientation()
    if orientation == Qt.Horizontal :
      self.process_splitter_horizontal = True
    else :
      self.process_splitter_horizontal = False

    for widget in self._process_widgets.values() :
      widget.setSplitterState(self._process_splitter_state)

    settings.endGroup()

#  def _writeSettings(self, settings, common):
#    settings.beginGroup(u"GuiProcessHandler")
#    settings.setValue(u"ProcessSplitterState",
#                      QVariant(self.main_process.splitter.saveState()))
#    settings.endGroup()

  # State & Data
  # ============================================================================

  def getTabProcess(self):
    return self.tab_process

  def getProcessWidgets(self):
    return self._process_widgets

  def getProcessWidget(self, label):
    return self._process_widgets[label]

  def setProcessInBackground(self, boolean=True):
    u"""
    Changes state of "process in background" check box

    :param boolean: "process in background" state
    :rtype: :obj:`bool`
    """
    self.pref.cb_process_in_background.setChecked(bool(boolean))

  def getProcessInBackground(self):
    u"""
    :return: check state (True or False) of "process in background" check box.
    :rtype: :obj:`bool`
    """
    return bool(self.pref.cb_process_in_background.checkState())

  def setMaxProcess(self, value):
    self.pref.sb_max_process.setValue(value)

  # Connections
  # ============================================================================

  def createTab (self, name, state=u'success'):
    gui_process = GuiProcess()
    self.addTab(gui_process, name, state)
    return gui_process

  def addTab(self, gui_process, name, state=u'success'):
    self.moveProcessSplitters() # save current state
    self._process_widgets[name] = gui_process
    self.append(gui_process)
    self.tab_process.addTab(gui_process, name)
    self.changeTabState(gui_process, state)
    try :
      gui_process.setSplitterState(self._process_splitter_state)
    except AttributeError : # first tab
      pass

  def _onActionCancelProcessTriggered (self):
    self.tab_process.currentWidget().cancelProcess()

  def _onActionCloseProcessTriggered (self):
    widget = self.tab_process.currentWidget()
    widget.cancelProcess()
    self.closeTab(widget)

  def _onCbViewLockedChanged (self, state):
    for widget in self._process_widgets.values() :
      widget.lockView(bool(state))

  def _onCbProcessInBackgroundChanged(self, state):
    self.emit(SIGNAL("processInBackgroundChanged"), bool(state))

  def _onMaxProcessChanged(self, value):
    self.emit(SIGNAL("maxProcessChanged"), int(value))

  def _onTabProcessCurrentChanged (self, index):
    gui_process = self.tab_process.widget(index)
    if index == 0 :
      self.action_close_process.setDisabled(True)
    else :
      self.action_close_process.setDisabled(False)
    if gui_process.running :
      self.action_cancel_process.setDisabled(False)
    else :
      self.action_cancel_process.setDisabled(True)
    self.moveProcessSplitters()

  def closeTab (self, tab):
    u"""
    :param tab: tab label or tab object you want to close
    :type tab: QString, unicode or QWidget
    """
    if isinstance(tab, QtGui.QWidget) :
      idx = self.tab_process.indexOf(tab)
      label = unicode(self.tab_process.tabText(idx))
      self.remove(tab)
    elif isinstance(tab, unicode) :
      label = unicode(tab)
    else :
      check_type(u'tab', tab, [unicode, QtGui.QWidget])

    self.emit(SIGNAL('processTabClosed'), label)

    self.tab_process.removeTab(idx)
    self._process_widgets.pop(label)
    tab.destroy()
    del tab

  def activateActions (self, boolean=None):
    self._activateActions(boolean)
    self._onTabProcessCurrentChanged(self.tab_process.currentIndex())

  def _activateActions(self, boolean=None):
    if boolean is None :
      boolean = self.isVisibleTo(self.parent())
    if boolean is True :
      self.action_cancel_process.setDisabled(False)
    else :
      self.action_close_process.setDisabled(True)

  def saveOutput (self):
    widget = self.tab_process.currentWidget()
    widget.saveOutput()

  def clearError(self):
    u"""
    Clear error view of current widget
    """
    widget = self.tab_process.currentWidget()
    widget.clearError()

  def clearOutput(self):
    u"""
    Clear standard output view of current widget
    """
    widget = self.tab_process.currentWidget()
    widget.clearOutput()

  def clearBothOutputs(self):
    u"""
    Clear both standard and error views of current widget
    """
    widget = self.tab_process.currentWidget()
    widget.clearOutput()
    widget.clearError()
    self.changeTabState(widget, u'default')

  def clearAll(self):
    u"""
    Clear all process widgets and reset state to default
    """
    for widget in self._process_widgets.values() :
      self.changeTabState(widget, u'default')
      widget.clearOutput()
      widget.clearError()

  def changeTabState(self, widget, state):
    u"""
    :param widget: GuiProcess widget
    :type widget: :class:`~pylot.qtgui.tools.GuiProcess`
    :param state: tab state : 'success', 'running', 'fail', 'default'
    :type state: str
    """
    check_type(u'widget', widget, GuiProcess)
    check_type(u'state', state, unicode)
    check_value(u'state', state, [u'success', u'running', u'fail', u'default'])

    tab_widget = self.tab_process
    widget.changeBackgroundColor(color=self.char_colors[state])

    idx = tab_widget.indexOf(widget)
    if state == u'success' :
      tab_widget.setTabIcon(idx, self._icon(u'LED_green.png'))
      #self.tab_process.setCurrentIndex(idx)
    elif state == u'running' :
      tab_widget.setTabIcon(idx, self._icon(u'LED_orange.png'))
    elif state == u'fail' :
      tab_widget.setTabIcon(idx, self._icon(u'LED_red.png'))
      #self.tab_process.setCurrentIndex(idx)
    elif state == u'default' :
      tab_widget.setTabIcon(idx, self._icon(u'LED_off.png'))
    else :
      pass
    self.activateActions()

  # Splitter states & moves
  # ============================================================================
  def _setprocess_splitter_horizontal (self, value):
    if value not in (True, False) :
      raise ValueError, error(tr_(u'process_splitter_horizontal: True or False required, got %s') % value)
      value = True

    self._process_splitter_horizontal = value
    self.pref.rb_horizontal.setChecked(value)
    self._setProcessSplitterHorizontal(value)

  process_splitter_horizontal = property (
    fget=lambda self: self._process_splitter_horizontal,
    fset=_setprocess_splitter_horizontal)

  def _setProcessSplitterHorizontal (self, value):
    assert(value in (True, False))
    if value is True :
      orientation = Qt.Horizontal
    else :
      orientation = Qt.Vertical
    idx = 0
    widget = self.tab_process.widget(idx)
    while widget :
      widget.splitter.setOrientation(orientation)
      idx += 1
      widget = self.tab_process.widget(idx)

  def moveProcessSplitters (self):
    u"""
    Look if a splitter in a process widget has been moved.
    If true, move all other process widget splitters and update
    _process_splitter_state attribute
    """
    # look if a splitter has been moved in a process widget
    widget_moved = None
    for widget in self._process_widgets.values() :
      if widget.splitter_moved is True :
        widget_moved = widget
        widget_moved.splitter_moved = False
        break

    # if a splitter has been moved
    if widget_moved :
      self._process_splitter_state = widget_moved.getSplitterState()
      for widget in self._process_widgets.values() :
        if widget != widget_moved :
          widget.setSplitterState(self._process_splitter_state)

  def processSplitterHorizontalChanged (self, state):
    self._process_split_horizontal = state
    self._setProcessSplitterHorizontal(state)

if __name__ == u"__main__":


#  app = QtGui.QApplication(sys.argv)
  win = QtGui.QMainWindow()

  widget = GuiProcessHandler()
  widget.createTab('name')

  win.setCentralWidget(widget)
  for tool_bar in widget.tool_bars :
    win.addToolBar(tool_bar)
  for menu in widget.menus :
    win.menuBar().addMenu(menu)

  if widget.pref :
    widget.pref.show()

  win.show()

#  res = app.exec_()

  import doctest
  doctest.testmod()
